package edu.scut.ga.reconf;

import java.util.ArrayList;
import java.util.Collection;

import org.eclipse.emf.ecore.change.util.ChangeRecorder;
import org.jenetics.CharacterGene;
import org.jenetics.Chromosome;
import org.jenetics.FitnessFunction;
import org.jenetics.Genotype;
import org.jgrapht.UndirectedGraph;
import org.jgrapht.alg.ConnectivityInspector;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.SimpleGraph;

import com.interpss.common.datatype.UnitType;
import com.interpss.core.CoreObjectFactory;
import com.interpss.core.aclf.AclfNetwork;
import com.interpss.core.algo.LoadflowAlgorithm;
import com.interpss.core.net.Branch;
import com.interpss.core.net.Bus;

import edu.scut.data.DistributionNetworkImporter;

public class LossFunction implements FitnessFunction<CharacterGene, Double> {
	
	private static final long serialVersionUID = 1L;
	
	private String networkName;
	private AclfNetwork newNetwork;
	private Collection<String> colInterconnectionID = new ArrayList<String>();
	
	public LossFunction(String networkName) {	// 需要在调用适应度函数计算类时加以初始化
		this.networkName = networkName;
		DistributionNetworkImporter dni = new DistributionNetworkImporter(networkName);
		this.newNetwork = dni.getNetwork();
		this.colInterconnectionID = dni.getColInterconnectionID();
	}

	public AclfNetwork getNewNetwork() {
		return newNetwork;
	}

	public Collection<String> getColInterconnectionID() {
		return colInterconnectionID;
	}

	public void setNewNetwork(AclfNetwork newNetwork) {
		this.newNetwork = newNetwork;
	}

	public void setColInterconnectionID(Collection<String> colInterconnectionID) {
		this.colInterconnectionID = colInterconnectionID;
	}

	@Override
	public Double evaluate(final Genotype<CharacterGene> genotype) {
		System.out.println("Current genotype: " + genotype.toText());
//		if (genotype.toText().toString().contains("0"))
//			System.out.println(genotype.toText() + " contains 0.");
//		else if (genotype.toText().toString().contains("1"))
//			System.out.println(genotype.toText() + " contains 1 but doesn't contain 0");
		// TODO: 这里重现调整过的配网结构时应参考Mike的新例子
		DistributionNetworkImporter dni = new DistributionNetworkImporter(networkName);
		this.newNetwork = dni.getNetwork();
		this.colInterconnectionID = dni.getColInterconnectionID();
//		ChangeRecorder recorder = new ChangeRecorder(newNetwork);
//		System.out.println("Branch number: " + newNetwork.getBranchList().size());
		// 1. 根据染色体编码得到重构后新网络拓扑
		// 1.1. 对染色体上的每个基因执行操作
		Chromosome<CharacterGene> firstChromosome = genotype.getChromosome();
		for (int i = 0; i < firstChromosome.length(); i++) {
			String thisInterconnectionID = (String) this.colInterconnectionID.toArray()[i];
			String leftBusID = newNetwork.getBranch(thisInterconnectionID).getFromBusId();
			String rightBusID = newNetwork.getBranch(thisInterconnectionID).getToBusId();
			// 1.2. 按照不同的基因代码断开不同的支路
			if (firstChromosome.getGene(i).toString().contains("0"))	// 打开联络开关
//				newNetwork.getAclfBranch(thisInterconnectionID).setStatus(false);
				newNetwork.removeBranch(thisInterconnectionID);
			else if (firstChromosome.getGene(i).toString().contains("1")) {	// 打开左分段开关
				for (Branch thisBranch : newNetwork.getBranchList()) {
					if ((thisBranch.getToBusId().equals(leftBusID) && (!thisBranch.getFromBusId().equals(rightBusID))) || 
							(thisBranch.getFromBusId().equals(leftBusID) && (!thisBranch.getToBusId().equals(rightBusID)))) {
//						newNetwork.getAclfBranch(thisBranch.getId()).setStatus(false);
						newNetwork.removeBranch(thisBranch.getId());
						break;
					}
				}
			}
			else if (firstChromosome.getGene(i).toString().contains("2")) {	// 打开右分段开关
				for (Branch thisBranch : newNetwork.getBranchList()) {
					if ((thisBranch.getToBusId().equals(rightBusID) && (!thisBranch.getFromBusId().equals(leftBusID))) || 
							(thisBranch.getFromBusId().equals(rightBusID) && (!thisBranch.getToBusId().equals(leftBusID)))) {
//						newNetwork.getAclfBranch(thisBranch.getId()).setStatus(false);
						newNetwork.removeBranch(thisBranch.getId());
						break;
					}
				}
			}
//			System.out.println("This gene: " + firstChromosome.getGene(i).toString() + ". Branch number after this gene: " + newNetwork.getBranchList().size());
		}
//		System.out.println("Branch number after reconfig: " + newNetwork.getBranchList().size());
		// 2. 仅对所得网络连通的情况执行计算
		if (isConnected(newNetwork)) {
			
//			// Test for temporary topologies
//		  	try {
//				FileWriter myFile = new FileWriter(new File("d://network" + genotype.toText() + ".txt"));
//				myFile.write(newNetwork.net2String());
//				myFile.flush();
//				FileWriter myObject = new FileWriter(new File("d://network_serialized" + genotype.toText() + ".txt"));
//				myObject.write(newNetwork.serialize());
//				myObject.flush();
//			} catch (IOException e) {
//				e.printStackTrace();
//			}
			
			// 2. 执行潮流计算
		  	LoadflowAlgorithm algo = CoreObjectFactory.createLoadflowAlgorithm(newNetwork);
		  	boolean pfSuccess = algo.loadflow();
		  	// 3. 计算网损
		  	if (pfSuccess) {	// 潮流计算成功
//			  	Double loss = 0.0;
//			  	for (Branch thisBranch : newNetwork.getBranchList()) {
//			  		double thisCurrent = newNetwork.getAclfBranch(thisBranch.getId()).current(UnitType.Amp);
//			  		double thisVB = newNetwork.getAclfBus(newNetwork.getAclfBranch(thisBranch.getId()).getFromBusId()).getBaseVoltage() / 
//			  				1000.0;
//			  		double thisSB = newNetwork.getBaseKva() / 1000.0;
//			  		double thisR = newNetwork.getAclfBranch(thisBranch.getId()).getZ().getReal() * thisVB * thisVB / thisSB;
//			  		loss += thisCurrent * thisCurrent * thisR;
//			  	}
//			  	loss /= 1000.0;
//			  	if (loss < 0.0001)
//			  		System.out.println();
////				  	System.out.println("Total loss = " + loss + "kW");
////			  	recorder.endRecording().apply();
//			  	System.out.println("Total loss=" + loss + "kVA");
//			  	if (loss == 0.0) {
//			  		System.out.println("Why?");
//			  	}
		  		double loss = newNetwork.totalLoss(UnitType.kVA).getReal();
			  	System.out.println("Total loss=" + loss + "kVA");
				return loss;
		  	}
		  	else {	// 潮流计算失败
//				  	System.out.println("Power flow calculation failed.");
//			  	recorder.endRecording().apply();
				return 1.0E10;
		  	}
		}
		else {
//		  	recorder.endRecording().apply();
			return 1.0E10;
		}
	}
	
	@Override
	public String toString() {
		return "Find minimum loss";
	}
	
	// 检验网络是否连通
	public boolean isConnected(AclfNetwork reconfNetwork) {
		// 1. 形成图对象
		// 1.1. 初始化图对象
		UndirectedGraph<String, DefaultEdge>  topo = new SimpleGraph<String, DefaultEdge>(DefaultEdge.class);
		// 1.2. 加入节点
		for (Bus thisBus : reconfNetwork.getBusList())
			topo.addVertex(thisBus.getId());
		// 1.3. 加入边
		for (Branch thisBranch : reconfNetwork.getBranchList())
			topo.addEdge(thisBranch.getFromBusId(), thisBranch.getToBusId());
		// 2. 判断连通性
		ConnectivityInspector<String, DefaultEdge> thisConnect = new ConnectivityInspector<String, DefaultEdge>(topo);
		return thisConnect.isGraphConnected();
	}

}